package b649a01.runners;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

import b649a01.poperators.*;

/***
 * 
 * An interpreter for parsing the TRITE code based on simple 5-stage pipelined
 * architecture.
 * 
 * @author Chao Sun
 * @version 1.0
 * 
 */
public class sim_p {
	private byte[] dumpArray;
	private int lineNum;
	private String[] lineArray;
	private int[] R;
	private double[] F;
	private int pipelineIndex;
	private int branchIndex = 0;
	private boolean branchFlag = false;
	private boolean IDstallFlag = false;
	private boolean EXEstallFlag = false;
	private boolean MEMstallFlag = false;
	private boolean WBstallFlag = false;
	private Hashtable<String, Integer> cycleHash;
	private Hashtable<String, Integer> branchTable;
	private LinkedList<Properties> readRegs;
	private LinkedList<Properties> writeRegs;
	private LinkedList<Properties> readMemOffs;
	private LinkedList<Properties> writeMemOffs;
	private LinkedList<Properties> busyComUnits;
	private LinkedList<Properties> fetchedIns;
	private LinkedList<calculator> insFetchedReg;
	private LinkedList<calculator> insToExe;
	private LinkedList<calculator> insToCycle;
	private LinkedList<calculator> insToMem;
	private LinkedList<calculator> insToWB;
	private int cycles;
	private int stalls;
	private int actualIns;

	/**
	 * Constructor. Initialize some of the fields.
	 */
	public sim_p() {
		dumpArray = new byte[1048576]; // 1MB of dump space
		R = new int[16]; // 16 integer registers
		R[0] = 0;
		F = new double[16]; // 16 floating point registers
		cycleHash = new Hashtable<String, Integer>();
		branchTable = new Hashtable<String, Integer>();
		readRegs = new LinkedList<Properties>();
		writeRegs = new LinkedList<Properties>();
		readMemOffs = new LinkedList<Properties>();
		writeMemOffs = new LinkedList<Properties>();
		busyComUnits = new LinkedList<Properties>();
		fetchedIns = new LinkedList<Properties>();
		insFetchedReg = new LinkedList<calculator>();
		insToExe = new LinkedList<calculator>();
		insToCycle = new LinkedList<calculator>();
		insToMem = new LinkedList<calculator>();
		insToWB = new LinkedList<calculator>();
		cycles = stalls = actualIns = 0;
		pipelineIndex = 0;
	}

	/**
	 * Read a file and interpret every line.
	 * 
	 * @param args
	 *          the command line string array
	 */
	private void run(String[] args) throws IOException {
		String insCycle = args[0];
		String memDump = args[1];
		String fileName = args[2];
		String line;

		// read the cycle number file
		BufferedReader cycle = new BufferedReader(new FileReader(insCycle));
		while ((line = cycle.readLine()) != null) {
			StringTokenizer ct = new StringTokenizer(line);
			cycleHash.put(ct.nextToken(), Integer.parseInt(ct.nextToken()));
		}

		// read memory dump file into an array
		BufferedReader dump = new BufferedReader(new FileReader(memDump));
		for (int i = 0; i < dumpArray.length; i++) {
			dumpArray[i] = (byte) dump.read();
		}

		// read lines in file to a linked list
		BufferedReader in = new BufferedReader(new FileReader(fileName));
		LinkedList<String> lineList = new LinkedList<String>();
		while ((line = in.readLine()) != null) {
			lineList.add(line);
		}
		lineNum = lineList.size();
		lineArray = new String[lineNum];
		// read the instruction parts into a string array
		for (int i = 0; i < lineNum; i++) {
			StringTokenizer lineTokenizer = new StringTokenizer(lineList.poll(), ";");
			String instruction = lineTokenizer.nextToken();
			lineArray[i] = instruction;
		}

		// each run of this loop is considered a cycle
		int i = 0;
		while (i < lineNum || !fetchedIns.isEmpty() || !insFetchedReg.isEmpty()
				|| !insToExe.isEmpty() || !insToCycle.isEmpty() || !insToMem.isEmpty()
				|| !insToWB.isEmpty() || branchFlag) {
			cycles++;
			// if a branch is predicted to be taken
			if (branchFlag) {
				i = branchIndex;
				branchFlag = false;
				cycles--;
				continue;
			}
			// fetch an instruction
			int fetchFlag = 0;
			if (i < lineNum && !IDstallFlag && !EXEstallFlag && !MEMstallFlag
					&& !WBstallFlag) {
				Properties property = fetch(i);
				i++;
				if (property != null) {
					fetchedIns.add(property);
					fetchFlag++;
				} else {
					cycles--;
					continue;
				}
			}
			// decode an older instruction and fetch registers
			int decodeFlag = 0;
			if ((fetchFlag == 0 || fetchedIns.size() > fetchFlag || !insFetchedReg
					.isEmpty())
					&& !EXEstallFlag && !MEMstallFlag && !WBstallFlag) {
				decodeFlag = decode();
				if (decodeFlag == -1) {
					System.exit(1);
				}
				if (IDstallFlag == true && insToExe.isEmpty() && insToCycle.isEmpty()
						&& insToMem.isEmpty() && insToWB.isEmpty()) {
					continue;
				}
			}
			// execute an even older instruction
			int exeFlag = 0;
			if ((insToExe.size() + insToCycle.size() > decodeFlag) && !MEMstallFlag
					&& !WBstallFlag) {
				exeFlag = execute();
				if (EXEstallFlag == true && insToMem.isEmpty() && insToWB.isEmpty()) {
					continue;
				}
			}
			// memory access and branch on an even even older instruction
			int memFlag = 0;
			if (insToMem.size() > exeFlag && !WBstallFlag) {
				memFlag = memoryAccess();
				if (MEMstallFlag == true && insToWB.isEmpty()) {
					continue;
				}
			}
			// write back on an even even even older instruction
			if (insToWB.size() > memFlag) {
				writeBack();
			}
		}

		printDump(); // printout dump area including computing results
		// write simulation results into file
		double CPI = (double) cycles / actualIns;
		FileWriter writer = new FileWriter(args[3]);
		writer.write("Simple 5-stage Pipeline Simulation Results \n\n"
				+ "Total Cycles: " + cycles + " \nTotal Stalls: " + stalls
				+ " \nActual Instructions: " + actualIns + " \nAverage CPI: " + CPI
				+ "\n");
		writer.flush();
	}

	/**
	 * Print out the computing results.
	 */
	private void printDump() {
		int index = 1;
		
		/*for (int j = 0; j < 300; j++) { // integer
			if (j % 10 == 0) {
				System.out.println("");
			}
			if (j % 100 == 0) {
				System.out.println("\n");
			}
			ByteBuffer buffer = ByteBuffer.allocate(4);
			buffer.put(dumpArray, j * 4, 4);
			System.out.print(buffer.getInt(0) + "\t");
	}*/

		for (int j = 0; j < 300; j++) { // floating point
			if (j % 10 == 0) {
				System.out.println("");
			}
			if (j % 100 == 0) {
				System.out.println("\nMatrix" + index++ + ":\n");
			}
			ByteBuffer buffer = ByteBuffer.allocate(8);
			buffer.put(dumpArray, j * 8, 8);
			System.out.print(buffer.getDouble(0) + "\t");
		}
		System.out.println("\n");
	}

	/**
	 * IF step. Fetch an instruction.
	 * 
	 * @param i
	 *          the index of the instruction
	 * @return a properties instance containing the operation and operands
	 */
	private Properties fetch(int i) {
		String instruction = lineArray[i];

		// break the instruction into operation and operand
		StringTokenizer insTokenizer = new StringTokenizer(instruction);
		String first = null;
		try {
			first = insTokenizer.nextToken();
		} catch (NoSuchElementException e) {
			return null;
		}
		String operation = null;
		String operand = null;
		StringTokenizer opTokenizer = null;

		// assign operation and operand
		if (first.endsWith(":")) { // this is a branch
			// maintain the branch index
			branchTable.put(first.substring(0, first.length() - 1), i);
			operation = insTokenizer.nextToken();
		} else { // other instruction
			operation = first;
		}
		try {
			operand = insTokenizer.nextToken();
			opTokenizer = new StringTokenizer(operand, ",");
		} catch (NoSuchElementException e) {
			opTokenizer = new StringTokenizer("NULL");
		}

		Properties property = new Properties();
		property.put("operation", operation);
		property.put("operands", opTokenizer);
		property.put("number", i);
		property.put("pipeIndex", pipelineIndex);
		pipelineIndex++;
		return property;
	}

	/**
	 * ID step. Decode instruction and fetch registers. Try assigning registers.
	 * Check on branch hazard. Exit on illegal operator.
	 * 
	 * @return an integer number indicating: (n) n instructions get ready to be
	 *         issued in this cycle; (-1) illegal operator
	 */
	private int decode() {
		int flag = 0;
		// in the IF step, try assign registers
		while (!insFetchedReg.isEmpty()) {
			calculator cal = insFetchedReg.getFirst();
			if (cal.getreadMemOffs().isEmpty() && cal.getreadRegs().isEmpty()) {
				insToExe.add(cal);
				insFetchedReg.removeFirst();
				flag++;
			} else if (rawHazard(cal) == false) { // check RAW data hazard
				cal.assignRegs();
				insToExe.add(cal);
				insFetchedReg.removeFirst();
				flag++;
			} else {
				stalls++; // stall on RAW data hazard
				break;
			}
		}

		// if no instruction needs fetching register, return
		if (fetchedIns.isEmpty()) {
			if (insFetchedReg.isEmpty()) {
				IDstallFlag = false;
			} else {
				stalls++;
			}
			return flag;
		}

		Properties property = fetchedIns.poll();
		String operation = property.getProperty("operation");
		StringTokenizer opTokenizer = (StringTokenizer) property.get("operands");
		int insNum = (Integer) property.get("number");
		int index = (Integer) property.get("pipeIndex");

		if (operation.equals("NOOP")) { // Do nothing
			noop noopCalculator = new noop(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, noopCalculator, flag);
			return flag;
		}

		// integer arithmetic operations
		if (operation.equals("ADD") || operation.equals("SUB")
				|| operation.equals("MUL") || operation.equals("DIV")) {
			arithmetic ariCalculator = new arithmetic(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, ariCalculator, flag);
			return flag;
		}

		if (operation.equals("L") || operation.equals("LI")) { // integer load
			load loadCalculator = new load(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, loadCalculator, flag);
			return flag;
		}

		if (operation.equals("S")) { // integer store
			store storeCalculator = new store(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, storeCalculator, flag);
			return flag;
		}

		if (operation.equals("BEQ") || operation.equals("BNEQ")
				|| operation.equals("BGTZ") || operation.equals("BLTZ")) { // branch
			stalls++; // stall one cycle on potential branch instruction
			IDstallFlag = true;
			branch branchCalculator = new branch(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, branchCalculator, flag);
			return flag;
		}

		// move from an integer register to a floating point register
		if (operation.equals("MOVE")) {
			move moveCalculator = new move(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, moveCalculator, flag);
			return flag;
		}

		// double precision floating point arithmetic operations
		if (operation.equals("ADD.D") || operation.equals("SUB.D")
				|| operation.equals("MUL.D") || operation.equals("DIV.D")) {
			f_arithmetic f_ariCalculator = new f_arithmetic(this, insNum, index,
					operation);
			flag = fetchAndTryAssignRegs(opTokenizer, f_ariCalculator, flag);
			return flag;
		}

		if (operation.equals("L.D")) { // floating point load
			f_load f_loadCalculator = new f_load(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, f_loadCalculator, flag);
			return flag;
		}

		if (operation.equals("S.D")) { // floating point store
			f_store f_storeCalculator = new f_store(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, f_storeCalculator, flag);
			return flag;
		}

		if (operation.equals("BEQ.D") || operation.equals("BNEQ.D")
				|| operation.equals("BGTZ.D") || operation.equals("BLTZ.D")) { // branch
			f_branch f_branchCalculator = new f_branch(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, f_branchCalculator, flag);
			stalls++;
			IDstallFlag = true;
			return flag;
		}

		// move from a floating point register to an integer register
		if (operation.equals("MOVE.D")) {
			f_move f_moveCalculator = new f_move(this, insNum, index, operation);
			flag = fetchAndTryAssignRegs(opTokenizer, f_moveCalculator, flag);
			return flag;
		}

		if (insFetchedReg.isEmpty() && !operation.startsWith("B")) {
			IDstallFlag = false;
		} else {
			stalls++;
		}

		System.err.println("Unknown operator! Exiting ...");
		return -1;
	}

	/**
	 * Decode a just fetched instruction by fetching registers and trying assign
	 * register values
	 * 
	 * @param opTokenizer
	 *          the string tokenizer containing all the operands
	 * @param cal
	 *          the calculator associated with this instruction
	 * @param flag
	 *          an integer value for maintaining the number of instructions that
	 *          are fully decoded
	 * @return the flag value
	 */
	private int fetchAndTryAssignRegs(StringTokenizer opTokenizer,
			calculator cal, int flag) {
		try {
			cal.fetchRegs(opTokenizer);
		} catch (NoSuchElementException e) { // NOOP
			flag++;
			if (insFetchedReg.isEmpty()) {
				insToExe.add(cal);
			} else {
				insFetchedReg.add(cal);
				IDstallFlag = true;
			}
			return flag;
		}
		if (rawHazard(cal) == false) {
			cal.assignRegs();
			flag++;
			if (cal.getOperator().equals("L.D") || cal.getOperator().equals("L")) {
				if (rawHazard(cal) == false) {
					if (insFetchedReg.isEmpty()) {
						insToExe.add(cal);
					} else {
						insFetchedReg.add(cal);
						IDstallFlag = true;
					}
				} else {
					stalls++; // stall on RAW data hazard
					IDstallFlag = true;
					insFetchedReg.add(cal);
				}
			} else {
				if (insFetchedReg.isEmpty()) {
					insToExe.add(cal);
				} else {
					insFetchedReg.add(cal);
					IDstallFlag = true;
				}
			}
		} else {
			stalls++; // stall on RAW data hazard
			IDstallFlag = true;
			insFetchedReg.add(cal);
		}
		return flag;
	}

	/**
	 * EXE step. Execute the instruction if no hazard exists.
	 * 
	 * @return an integer indicating: (n) n instructions finished execution in
	 *         this cycle
	 * @throws IOException
	 */
	private int execute() throws IOException {
		int flag = 0;
		LinkedList<Properties> comUnitsToRemove = new LinkedList<Properties>();

		int cycleSize = insToCycle.size();
		for (int i = 0; i < cycleSize;) { // cycle
			calculator cal = insToCycle.get(i);
			cal.execute();
			if (cal.getcycleNum() <= 0 && i == 0) { // finished execution
				insToMem.add(cal);
				insToCycle.remove(i);
				cycleSize = insToCycle.size();
				flag++;
			} else {
				i++;
			}
		}

		while (!insToExe.isEmpty()) { // execute
			calculator cal = insToExe.getFirst();
			if (structureHazard(cal) == false) {
				cal.execute(); // good to issue or continue execution
				Properties property = cal.getcomUnitProperty();
				comUnitsToRemove.add(property);
				if (cal.getcycleNum() <= 0) { // finished execution
					insToMem.add(cal);
					flag++;
				} else {
					insToCycle.add(cal);
					EXEstallFlag = true;
				}
				insToExe.removeFirst();
			} else {
				stalls++;
				EXEstallFlag = true;
				break; // stalls at EXE, stall all others following this one
			}
		}
		if (insToExe.isEmpty() && insToCycle.isEmpty()) {
			EXEstallFlag = false;
		} else {
			stalls++;
		}
		// remove the computing unit locks
		while (!comUnitsToRemove.isEmpty()) {
			removebusyComUnits(comUnitsToRemove.poll());
		}
		return flag;
	}

	/**
	 * MEM step. Write results into memory. Check WAR or WAW memory hazard.
	 * 
	 * @return an integer indicating: (n) n instructions finished memory access in
	 *         this cycle
	 * @throws IOException
	 */
	private int memoryAccess() throws IOException {
		int flag = 0;
		while (!insToMem.isEmpty()) {
			calculator cal = insToMem.getFirst();
			if (warwMemHazard(cal) == true) {
				stalls++;
				MEMstallFlag = true;
				break;
			} else {
				cal.memory();
				if (cal.getaliveFlag()) {
					insToWB.add(cal);
				} else { // some instructions are finished in MEM step
					actualIns++;
				}
				insToMem.removeFirst();
				flag++;
				if (branchFlag) {
					flushIns(cal.getpipelineIndex(), flag);
					break; // cut off when branching
				}
			}
		}
		if (insToMem.isEmpty()) {
			MEMstallFlag = false;
		} else {
			stalls++;
		}
		return flag;
	}

	/**
	 * When taking a branch, flush all the temporary tables. Only keep those
	 * fetched after the current one.
	 * 
	 */
	private void flushIns(int index, int flag) {
		readRegs.clear();
		readMemOffs.clear();
		writeMemOffs.clear();
		busyComUnits.clear();
		fetchedIns.clear();
		insFetchedReg.clear();
		insToExe.clear();
		insToCycle.clear();
		insToMem.clear();

		IDstallFlag = EXEstallFlag = MEMstallFlag = WBstallFlag = false;

		for (int i = 0; i < writeRegs.size();) {
			Properties p = writeRegs.get(i);
			if ((Integer) p.get("pipeIndex") > index) {
				writeRegs.remove(i);
			} else {
				i++;
			}
		}

		int insToWBsize = insToWB.size();
		if (insToWBsize < flag) {
			return;
		}
		while (!insToWB.isEmpty()) {
			calculator cal = insToWB.getFirst();
			if (warwRegHazard(cal) == false && cal.getpipelineIndex() < index) {
				cal.writeBack();
				insToWB.removeFirst();
				actualIns++;
			} else {
				WBstallFlag = true;
				break;
			}
		}
		if (insToWB.isEmpty()) {
			WBstallFlag = false;
		} else {
			stalls++;
		}
	}

	/**
	 * WB step. Write results into registers. Check WAR or WAW register hazard.
	 */
	private void writeBack() {
		while (!insToWB.isEmpty()) {
			calculator cal = insToWB.getFirst();
			if (warwRegHazard(cal) == true) {
				WBstallFlag = true;
				break;
			} else {
				cal.writeBack();
				insToWB.removeFirst();
				actualIns++;
				WBstallFlag = false;
			}
		}
		if (insToWB.isEmpty()) {
			WBstallFlag = false;
		} else {
			stalls++;
		}
	}

	/**
	 * Check for RAW data hazard. Call this on the ID step.
	 * 
	 * @param cal
	 *          the operator to be checked on
	 * 
	 * @return a boolean value indicating if there is a RAW data hazard
	 */
	@SuppressWarnings("unchecked")
	private boolean rawHazard(calculator cal) {
		// check the registers
		Vector<String> regs = cal.getreadRegs();
		int insIndex = cal.getpipelineIndex();
		int regLenth = writeRegs.size();
		for (int j = 0; j < regs.size(); j++) {
			String reg = regs.get(j);
			for (int k = 0; k < regLenth; k++) {
				Properties property = writeRegs.get(k);
				if ((Integer) property.get("pipeIndex") < insIndex) {
					Vector<String> vec = (Vector<String>) property.get("vector");
					if (vec.contains(reg)) {
						return true;
					}
				}
			}
		}
		// check the memory
		Vector<Integer> memOffs = cal.getreadMemOffs();
		int memLenth = writeMemOffs.size();
		for (int j = 0; j < memOffs.size(); j++) {
			Integer offset = memOffs.get(j);
			for (int k = 0; k < memLenth; k++) {
				Properties property = writeMemOffs.get(k);
				if ((Integer) property.get("pipeIndex") < insIndex) {
					Vector<Integer> vec = (Vector<Integer>) property.get("vector");
					if (vec.contains(offset)) {
						return true;
					}
				}
			}
		}
		// no hazard
		return false;
	}

	/**
	 * Check for structure hazard. Call this on the EXE step.
	 * 
	 * @param cal
	 *          the operator to be checked on
	 * 
	 * @return a boolean value indicating if there is a structure hazard
	 */
	private boolean structureHazard(calculator cal) {
		String comUnit = cal.getbusyComUnit();
		if (comUnit == null) {
			return false;
		}
		int insIndex = cal.getpipelineIndex();
		int unitLenth = busyComUnits.size();
		for (int j = 0; j < unitLenth; j++) {
			Properties property = busyComUnits.get(j);
			if ((Integer) property.get("pipeIndex") < insIndex) {
				String unit = property.getProperty("unit");
				if (unit.equals(comUnit)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Check for memory WAR and WAW data hazard. Call this on the MEM step.
	 * 
	 * @param cal
	 *          the operator to be checked on
	 * 
	 * @return a boolean value indicating if there is a WAR or WAW data hazard
	 */
	@SuppressWarnings("unchecked")
	private boolean warwMemHazard(calculator cal) {
		Vector<Integer> memOffs = cal.getwriteMemOffs();
		int insIndex = cal.getpipelineIndex();
		int readLenth = readMemOffs.size();
		int writeLenth = writeMemOffs.size();

		for (int j = 0; j < memOffs.size(); j++) {
			Integer offset = memOffs.get(j);
			for (int k = 0; k < readLenth; k++) {
				Properties property = readMemOffs.get(k);
				if ((Integer) property.get("pipeIndex") < insIndex) {
					Vector<Integer> vec = (Vector<Integer>) property.get("vector");
					if (vec.contains(offset)) {
						return true;
					}
				}
			}
			for (int k = 0; k < writeLenth; k++) {
				Properties property = writeMemOffs.get(k);
				if ((Integer) property.get("pipeIndex") < insIndex) {
					Vector<Integer> vec = (Vector<Integer>) property.get("vector");
					if (vec.contains(offset)) {
						return true;
					}
				}
			}
		}
		return false; // no hazard
	}

	/**
	 * Check for register WAR and WAW data hazard. Call this on the WB step.
	 * 
	 * @param cal
	 *          the operator to be checked on
	 * 
	 * @return a boolean value indicating if there is a WAR or WAW data hazard
	 */
	@SuppressWarnings("unchecked")
	private boolean warwRegHazard(calculator cal) {
		Vector<String> regs = cal.getwriteRegs();
		int insIndex = cal.getpipelineIndex();
		int readLenth = readRegs.size();
		int writeLenth = writeRegs.size();

		for (int j = 0; j < regs.size(); j++) {
			String reg = regs.get(j);
			for (int k = 0; k < writeLenth; k++) {
				Properties property = writeRegs.get(k);
				if ((Integer) property.get("pipeIndex") < insIndex) {
					Vector<String> vec = (Vector<String>) property.get("vector");
					if (vec.contains(reg)) {
						return true;
					}
				}
			}
			for (int k = 0; k < readLenth; k++) {
				Properties property = readRegs.get(k);
				if ((Integer) property.get("pipeIndex") < insIndex) {
					Vector<String> vec = (Vector<String>) property.get("vector");
					if (vec.contains(reg)) {
						return true;
					}
				}
			}
		}
		return false; // no hazard
	}

	/**
	 * Try to find a branch by name in the instruction array.
	 * 
	 * @param br
	 *          the label of the branch to be searched for
	 * @return an integer value indicating: (n) the index of this branch in the
	 *         instruction array; (-1) no such branch
	 */
	public int searchBranch(String br) {
		for (int i = 0; i < lineNum; i++) {
			String instruction = lineArray[i];
			StringTokenizer insTokenizer = new StringTokenizer(instruction);
			String first = insTokenizer.nextToken();
			if (first.endsWith(":")) {
				String bra = first.substring(0, first.length() - 1);
				if (bra.equals(br)) {
					branchTable.put(bra, i);
					return i;
				}
			}
		}
		return -1;
	}

	public byte[] getdumpArray() {
		return dumpArray;
	}

	public void memdumpArray(byte value, int offset) {
		this.dumpArray[offset] = value;
	}

	public int[] getR() {
		return R;
	}

	public void wbR(int value, int index) {
		this.R[index] = value;
	}

	public double[] getF() {
		return F;
	}

	public void wbF(double value, int index) {
		this.F[index] = value;
	}

	public Hashtable<String, Integer> getcycleHash() {
		return cycleHash;
	}

	public Hashtable<String, Integer> getbranchTable() {
		return branchTable;
	}

	public void addbranchTable(String branch, int number) {
		branchTable.put(branch, number);
	}

	public void aadreadRegs(calculator cal, Properties property) {
		readRegs.add(property);
	}

	public void removereadRegs(Properties property) {
		readRegs.remove(property);
	}

	public void addwriteRegs(calculator cal, Properties property) {
		writeRegs.add(property);
	}

	public void removewriteRegs(Properties property) {
		writeRegs.remove(property);
	}

	public void addreadMemOffs(calculator cal, Properties property) {
		readMemOffs.add(property);
	}

	public void removereadMemOffs(Properties property) {
		readMemOffs.remove(property);
	}

	public void addwriteMemOffs(calculator cal, Properties property) {
		writeMemOffs.add(property);
	}

	public void removewriteMemOffs(Properties property) {
		writeMemOffs.remove(property);
	}

	public void addbusyComUnits(calculator cal, Properties property) {
		busyComUnits.add(property);
	}

	public void removebusyComUnits(Properties property) {
		busyComUnits.remove(property);
	}

	public void setbranchIndex(int index) {
		this.branchIndex = index;
	}

	public void setbranchFlag() {
		this.branchFlag = true;
	}

	/**
	 * main method for testing.
	 * 
	 * @param args
	 *          the command line string array
	 */
	public static void main(String args[]) {
		if (args.length != 4) {
			System.err.println("Args error! Usage: java sim_p "
					+ "<ARCH_PARAMS> <INIT_MEM> <TRITE> <OUTPUT>");
			System.exit(1);
		}

		try {
			sim_p simulator = new sim_p();
			simulator.run(args);
		} catch (FileNotFoundException e) {
			System.err.println("File not found! Exiting ...");
			e.printStackTrace();
		} catch (IOException e) {
			System.err.println("File reading error! Exiting ...");
			e.printStackTrace();
		}
	}

}